{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "D = {'death_rate': np.array([6.2300e-03, 4.4000e-04, 2.7000e-04, 2.0000e-04, 1.6000e-04,\n",
    "       1.2000e-04, 1.1000e-04, 1.1000e-04, 1.2000e-04, 1.1000e-04,\n",
    "       1.0000e-04, 1.3000e-04, 1.3000e-04, 1.5000e-04, 2.0000e-04,\n",
    "       2.5000e-04, 3.7000e-04, 4.7000e-04, 6.4000e-04, 7.1000e-04,\n",
    "       7.6000e-04, 8.7000e-04, 8.7000e-04, 8.8000e-04, 9.4000e-04,\n",
    "       9.2000e-04, 9.5000e-04, 9.3000e-04, 9.9000e-04, 1.0100e-03,\n",
    "       1.0300e-03, 1.0900e-03, 1.1000e-03, 1.1400e-03, 1.1500e-03,\n",
    "       1.2000e-03, 1.3100e-03, 1.3700e-03, 1.4600e-03, 1.5600e-03,\n",
    "       1.6200e-03, 1.8500e-03, 2.0100e-03, 2.1600e-03, 2.4300e-03,\n",
    "       2.5800e-03, 2.9800e-03, 3.2500e-03, 3.5100e-03, 3.8700e-03,\n",
    "       4.1300e-03, 4.5400e-03, 4.9400e-03, 5.3300e-03, 5.7100e-03,\n",
    "       6.0200e-03, 6.7000e-03, 7.1000e-03, 7.6900e-03, 8.2800e-03,\n",
    "       8.6000e-03, 9.3200e-03, 9.9800e-03, 1.1010e-02, 1.2500e-02,\n",
    "       1.2820e-02, 1.4040e-02, 1.5150e-02, 1.6870e-02, 1.8300e-02,\n",
    "       1.9670e-02, 2.1330e-02, 2.3470e-02, 2.5620e-02, 2.8000e-02,\n",
    "       3.0830e-02, 3.4410e-02, 3.7110e-02, 4.1260e-02, 4.4480e-02,\n",
    "       4.9640e-02, 5.5390e-02, 6.1490e-02, 6.8030e-02, 7.6730e-02,\n",
    "       8.5610e-02, 9.5400e-02, 1.0636e-01, 1.1802e-01, 1.3385e-01,\n",
    "       1.5250e-01, 1.6491e-01, 1.8738e-01, 2.0757e-01, 2.2688e-01,\n",
    "       2.5196e-01, 2.7422e-01, 2.9239e-01, 3.2560e-01, 3.4157e-01]), 'population': np.array([3.94415, 3.97807, 4.09693, 4.11904, 4.06317, 4.05686, 4.06638,\n",
    "       4.03058, 4.04649, 4.14835, 4.17254, 4.11442, 4.10624, 4.11801,\n",
    "       4.16598, 4.24282, 4.31614, 4.39529, 4.50085, 4.58523, 4.51913,\n",
    "       4.35429, 4.26464, 4.19857, 4.24936, 4.26235, 4.15231, 4.24887,\n",
    "       4.21525, 4.22308, 4.28567, 3.97022, 3.98685, 3.88015, 3.83922,\n",
    "       3.95643, 3.80209, 3.93445, 4.12188, 4.3648 , 4.38327, 4.11498,\n",
    "       4.0761 , 4.10511, 4.2115 , 4.50887, 4.51976, 4.53526, 4.5388 ,\n",
    "       4.6059 , 4.66029, 4.46463, 4.50085, 4.38035, 4.292  , 4.25471,\n",
    "       4.03751, 3.93639, 3.79493, 3.64127, 3.62113, 3.4926 , 3.56318,\n",
    "       3.48388, 2.65713, 2.68076, 2.63914, 2.64936, 2.32367, 2.14232,\n",
    "       2.04312, 1.94932, 1.86427, 1.73696, 1.68449, 1.62008, 1.47107,\n",
    "       1.45533, 1.40012, 1.37119, 1.30851, 1.21287, 1.16142, 1.07481,\n",
    "       0.98572, 0.91472, 0.81421, 0.71291, 0.64062, 0.538  , 0.43556,\n",
    "       0.34499, 0.28139, 0.21698, 0.16944, 0.12972, 0.09522, 0.06814,\n",
    "       0.0459 , 0.03227]), 'birth_rate': np.array([0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.0002 , 0.0002 , 0.0002 , 0.0002 ,\n",
    "       0.0002 , 0.0171 , 0.0171 , 0.0171 , 0.0171 , 0.0171 , 0.045  ,\n",
    "       0.045  , 0.045  , 0.045  , 0.045  , 0.05415, 0.05415, 0.05415,\n",
    "       0.05415, 0.05415, 0.04825, 0.04825, 0.04825, 0.04825, 0.04825,\n",
    "       0.0225 , 0.0225 , 0.0225 , 0.0225 , 0.0225 , 0.0051 , 0.0051 ,\n",
    "       0.0051 , 0.0051 , 0.0051 , 0.00035, 0.00035, 0.00035, 0.00035,\n",
    "       0.00035, 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
    "       0.     , 0.     ])}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import numpy.linalg as npl\n",
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 10.1 Matrix-Matrix Multiplication"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 3.5, -4.5],\n",
       "       [-1. ,  1. ]])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A,B = np.array([[-1.5,3,2],[1,-1,0]]), np.array([[-1,-1],[0,-2],[1,0]])\n",
    "C = np.matmul(A,B) #J: A*B\n",
    "C"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = np.random.randn(10,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(6.181005057839912, 6.181005057839911)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G = np.matmul(A.transpose(),A) #ATA; J: A'*A\n",
    "G[1,1], npl.norm(A[:,1])**2 #Gram matrix (ii) = norm of col i squared"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-0.9037181198791941, -0.9037181198791941, -0.9037181198791941)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G[0,2], np.matmul(A[:,0].transpose(),A[:,2]), A[:,0] @ A[:,2] #(ij) = inner of i and j (inner and matmul + transpose are different see: https://docs.scipy.org/doc/numpy/reference/generated/numpy.matmul.html)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "m,n,o,p = 2000,50,2000,2000\n",
    "A,B,C = np.random.randn(m,n), np.random.randn(n,p), np.random.randn(p,o)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "timing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "LHS = np.matmul(np.matmul(A,B),C)\n",
    "RHS = np.matmul(A,np.matmul(B,C))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "131 ms ± 14.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit np.matmul(np.matmul(A,B),C)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "21.1 ms ± 223 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit np.matmul(A,np.matmul(B,C))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LHS and RHS are different by a magnitude of 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5.237905887910135e-10"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "npl.norm(LHS-RHS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20.1 ms ± 1.68 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit npl.multi_dot([A,B,C])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "triple mult shows the slower order is used"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 10.2 Composition of Linear Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1., -2.,  1.,  0.,  0.],\n",
       "       [ 0.,  1., -2.,  1.,  0.],\n",
       "       [ 0.,  0.,  1., -2.,  1.]])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "D = lambda n: np.block([-(np.identity(n-1)),np.vstack(np.zeros(n-1))]) + np.block([np.vstack(np.zeros(n-1)),np.identity(n-1)])\n",
    "negativediag = np.block([-(np.identity(n-1))])\n",
    "positivediag = np.block([np.vstack(np.zeros(n-1)),np.identity(n-1)])\n",
    "mergeddiag = D(5)\n",
    "# positivediag, negativediag, mergeddiag\n",
    "\n",
    "delta = np.matmul(D(4),D(5)) #second difference matrix\n",
    "delta "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 10.3 Matrix Power"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "matrix([[1, 0, 1, 1, 0],\n",
       "        [0, 1, 1, 1, 2],\n",
       "        [1, 0, 1, 2, 1],\n",
       "        [0, 1, 0, 0, 1],\n",
       "        [1, 0, 0, 0, 0]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "matrix([[18, 11, 15, 20, 20],\n",
       "        [25, 14, 21, 28, 26],\n",
       "        [24, 14, 20, 27, 26],\n",
       "        [11,  6,  9, 12, 11],\n",
       "        [ 6,  4,  5,  7,  7]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "array([18, 14, 20, 12,  7])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#finding how many cycles of len(8) there are in an adjacency matrix \n",
    "A = np.matrix([[0,1,0,0,1],[1,0,1,0,0],[0,0,1,1,1],[1,0,0,0,0],[0,0,0,1,0]])\n",
    "A**2\n",
    "A**8\n",
    "\n",
    "np.diag(A**8) # number_of_cycles "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.0002 , 0.0002 , 0.0002 , 0.0002 ,\n",
       "       0.0002 , 0.0171 , 0.0171 , 0.0171 , 0.0171 , 0.0171 , 0.045  ,\n",
       "       0.045  , 0.045  , 0.045  , 0.045  , 0.05415, 0.05415, 0.05415,\n",
       "       0.05415, 0.05415, 0.04825, 0.04825, 0.04825, 0.04825, 0.04825,\n",
       "       0.0225 , 0.0225 , 0.0225 , 0.0225 , 0.0225 , 0.0051 , 0.0051 ,\n",
       "       0.0051 , 0.0051 , 0.0051 , 0.00035, 0.00035, 0.00035, 0.00035,\n",
       "       0.00035, 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     ,\n",
       "       0.     , 0.     ])"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "array([0.99214136, 0.99846098, 0.99896916, 0.99930754, 0.99955601,\n",
       "       0.99971453, 1.01666352, 1.03337697, 1.04998068, 1.06641671,\n",
       "       1.08276217, 1.12665271, 1.17026879, 1.21384417, 1.25739883,\n",
       "       1.30092607, 1.33682987, 1.37278246, 1.40887169, 1.44513455,\n",
       "       1.48148108, 1.48433668, 1.48732153, 1.4902904 , 1.49322704,\n",
       "       1.49623422, 1.4645592 , 1.43294304, 1.40121367, 1.36944629,\n",
       "       1.33757135, 1.29428106, 1.25090241, 1.20733845, 1.16362983,\n",
       "       1.11962   , 1.09637065, 1.07285263, 1.04911008, 1.02518117,\n",
       "       1.00097489, 0.99349625, 0.98584642, 0.97796344, 0.96984749,\n",
       "       0.96162445, 0.95796998, 0.95405653, 0.95003034, 0.94570202,\n",
       "       0.94116952, 0.93694505, 0.93244602, 0.92772317, 0.92242546,\n",
       "       0.91612622, 0.90985884, 0.90313542, 0.8958132 , 0.8875259 ,\n",
       "       0.87855864, 0.86874863, 0.85821681, 0.84652275, 0.83401737,\n",
       "       0.82092647, 0.80594958, 0.78929861, 0.77169897, 0.75255426,\n",
       "       0.73248513, 0.71009208, 0.68537922, 0.65869482, 0.63002505,\n",
       "       0.59843953, 0.56461418, 0.5289512 , 0.4909096 , 0.4516057 ,\n",
       "       0.40936692, 0.36506005, 0.32273424, 0.27944327, 0.23760339,\n",
       "       0.19896231, 0.16276618, 0.1305908 , 0.10340557, 0.07906837,\n",
       "       0.        , 0.        , 0.        , 0.        , 0.        ,\n",
       "       0.        , 0.        , 0.        , 0.        , 0.        ])"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1270135c0>]"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#how much each age bracket contributes to the population\n",
    "b = D[\"birth_rate\"]\n",
    "d = D[\"death_rate\"]\n",
    "A = np.block([[b],[np.diag(1-d[0:len(d)-1]),np.vstack(np.zeros(len(d)-1))]])\n",
    "\n",
    "cf = np.ones(100) @ npl.matrix_power(A,10) #contribution factor\n",
    "\n",
    "A[0]\n",
    "cf\n",
    "plt.plot(cf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 10.4 QR Factorization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Q,R = npl.qr(A)\n",
    "A = np.random.randn(6,4)\n",
    "Q,R = npl.qr(A)\n",
    "R\n",
    "Q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "npl.norm(np.matmul(Q,R)-A)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.matmul(Q.transpose(),Q)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
